#include <gtest/gtest.h>

#include <utility>

std::unique_ptr<int> return_unique_ptr(int val) {
    std::unique_ptr<int> up(new int(val));
    return up;
}

//假设有一个Socket类, 需要回收资源
class Socket {
private:
    std::string name;
public:
    explicit Socket(std::string name) : name(std::move(name)) {}

    std::string &getName() { return name; }

    void close() {}
};

TEST(unique_ptr, SMART_POINTER) {
    int *ap = new int(123);
    printf("%p, %d\n", ap, *ap);

    // std::unique_ptr 对其持有的堆内存具有唯一拥有权
    // std::unique_ptr 对象销毁时会释放其持有的堆内存
    //初始化方式1
    std::unique_ptr<int> sp1(new int(123));
    std::cout << sp1 << std::endl;
    //初始化方式2
    std::unique_ptr<int> sp2;
    sp2.reset(new int(123));
    std::cout << sp2 << std::endl;
    //初始化方式3
    //是123, 不是new int(123)
    std::unique_ptr<int> sp3 = std::make_unique<int>(123);
    std::cout << sp3 << std::endl;

    // std::unique_ptr 禁止复制语义
    // 禁止复制语义也存在特例，即可以通过一个函数返回一个 std::unique_ptr
    const std::unique_ptr<int> &sp4 = return_unique_ptr(123);
    std::cout << sp4 << std::endl;

    // 移动构造
    std::unique_ptr<int> sp5(std::move(sp1));
    std::unique_ptr<int> sp6 = std::move(sp5); // 不能move sp1 否则是空指针

    std::cout << sp5 << "\n" << sp6 << std::endl;

    // std::unique_ptr 不仅可以持有一个堆对象，也可以持有一组堆对象
    //创建10个int类型的堆对象, 别忘了模板类型 为 int[]
    std::unique_ptr<int[]> sp7(new int[10]);
    std::unique_ptr<int[]> sp8 = std::make_unique<int[]>(10);
    std::cout << sp7 << "\n" << sp8 << std::endl;

    for (int i = 0; i < 10; i++) {
        sp7[i] = i;
        sp8[i] = i;
    }

    for (int i = 0; i < 10; ++i) {
        std::cout << sp7[i] << ", " << sp8[i] << std::endl;
    }

    // 自定义资源关闭
    auto deleter = [](Socket *socket) {
        socket->close();
        printf("%s socket close\n", socket->getName().c_str());
        delete socket;
    };
    std::unique_ptr<Socket, void (*)(Socket *)> sp_socket_1(new Socket("1"), deleter);
    // 可以使用 decltype(deleter) 让编译器自己推导 deleter 的类型
    std::unique_ptr<Socket, decltype(deleter)> sp_socket_2(new Socket("2"), deleter);
    // 先是2释放, 再是1

    // std::unique_ptr 重写了operator-> 和 operator 方法, 可直接操作对象
    // 直接使用
    std::cout << sp_socket_1->getName() << std::endl;

    /*
     * std::unique_ptr 对其持有的资源具有独占性
     * 而 std::shared_ptr 持有的资源可以在多个 std::shared_ptr 之间共享
     * 每多一个 std::shared_ptr 对资源的引用，资源引用计数将增加 1
     * 每一个指向该资源的 std::shared_ptr 对象析构时，资源引用计数减 1
     * 最后一个 std::shared_ptr 对象析构时，发现资源计数为 0，将释放其持有的资源
     * 引用计数是线程安全的
     *
     * std::shared_ptr 提供了一个 use_count() 方法来获取当前持有资源的引用计数
     */

    /*
     * std::weak_ptr 的正确使用场景
     * 资源如果可能就使用，如果不可使用则不用的场景
     */
}
